<?php

namespace Registro\Acl;

use Phalcon\Acl\Role as AclRole;
use Phalcon\Acl\Resource as AclResource;
use Phalcon\Acl\Adapter\Memory as AclList;
use Phalcon\Mvc\User\Component;
use Registro\Models\UserProfiles;

/**
 * Registro\Acl\Acl
 */
class Acl extends Component {

    /**
     * The ACL Object
     *
     * @var \Phalcon\Acl\Adapter\Memory
     */
    private $acl;

    /**
     * The filepath of the ACL cache file from APP_DIR
     *
     * @var string
     */
    private $filePath = '';

    /**
     * Define the resources that are considered "private". These controller => actions require authentication.
     *
     * @var array
     */
    private $privateResources = array(
        'instruments' => array(
            'index'
        )
    );
    //Public area resources
    private $publicResources = array(
        'index' => array( 'index' ),
        'auth' => array( 'index', 'login', 'logout' ),
        'register' => array( 'index' ),
        'exceptions' => array( 'index', 'show401', 'show404', 'show500' ),
        'session' => array( 'index', 'register', 'start', 'end' )
    );

    /**
     * Human-readable descriptions of the actions used in {@see $privateResources}
     *
     * @var array
     */
    private $actionDescriptions = array(
        'index' => 'Access',
        'search' => 'Search',
        'create' => 'Create',
        'edit' => 'Edit',
        'delete' => 'Delete',
        'changePassword' => 'Change password'
    );

    /**
     * Returns an existing or new ACL list
     *
     * @return Phalcon\Acl\Adapter\Memory
     */
    public function __construct()
    {
        $this->filePath = $this->globalConfig->application->apcAclFilePath;
    }

    /**
     * Checks if a controller is private or not
     *
     * @param string $controllerName
     * @return boolean
     */
    public function isPrivate( $controllerName )
    {
        return isset( $this->privateResources[$controllerName] );
    }

    /**
     * Checks if the current profile is allowed to access a resource
     *
     * @param string $profile
     * @param string $controller
     * @param string $action
     * @return boolean
     */
    public function isAllowed( $profile, $controller, $action )
    {
        return $this->getAcl()->isAllowed( $profile, $controller, $action );
    }

    public function getAcl()
    {
        // Check if the ACL is already created
        if( is_object( $this->acl ) )
        {
            return $this->acl;
        }

        // Check if the ACL is in APC
        if( function_exists( 'apc_fetch' ) )
        {
            $acl = apc_fetch( 'registro-acl' );
            if( is_object( $acl ) )
            {
                $this->acl = $acl;
                return $acl;
            }
        }

        /**
         * @todo TEST! Eliminare
         */
        // Check if the ACL is already generated
//        if( !file_exists( APP_DIR . $this->filePath ) )
//        {
        $this->acl = $this->rebuild();
        return $this->acl;
//        }
//
//        // Get the ACL from the data file
//        $data = file_get_contents( APP_DIR . $this->filePath );
//        $this->acl = unserialize( $data );
        // Store the ACL in APC
        if( function_exists( 'apc_store' ) )
        {
            apc_store( 'registro-acl', $this->acl );
        }

        return $this->acl;
    }

    /**
     * Rebuilds the access list into a file
     *
     * @return \Phalcon\Acl\Adapter\Memory
     */
    public function rebuild()
    {
        $acl = new AclList();

        $acl->setDefaultAction( \Phalcon\Acl::DENY );

        // Register roles
        //$profiles = UserGroups::find( 'active = "Y"' );
        $profiles = UserProfiles::find();

        foreach( $profiles as $profile ){
            $acl->addRole( new AclRole( $profile->profile_name ) );
        }

        foreach( $this->privateResources as $resource => $actions ){
            $acl->addResource( new AclResource( $resource ), $actions );
        }

        foreach( $this->publicResources as $resource => $actions ){
            $acl->addResource( new AclResource( $resource ), $actions );
        }

        // Grant acess to private area to role Users
        foreach( $profiles as $profile ){

            foreach( $this->publicResources as $resource => $actions ){
                $acl->allow( $profile->profile_name, $resource, $actions );
            }
            // Grant permissions in "permissions" model
//            foreach( $profile->getPermissions() as $permission ){
//                $acl->allow( $profile->name, $permission->resource, $permission->action );
//            }
            // Always grant these permissions
//            $acl->allow( $profile->name, 'users', 'changePassword' );
        }

        if( touch( APP_DIR . $this->filePath ) && is_writable( APP_DIR . $this->filePath ) )
        {

            file_put_contents( APP_DIR . $this->filePath, serialize( $acl ) );

            // Store the ACL in APC
            if( function_exists( 'apc_store' ) )
            {
                apc_store( 'registro-acl', $acl );
            }
        }
        else
        {
            $this->flash->error(
                    'The user does not have write permissions to create the ACL list at ' . APP_DIR . $this->filePath
            );
        }

        return $acl;
    }

}
